package com.leilei.auth;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.leilei.entity.LoginUserVO;
import com.leilei.entity.ResponseCodeEnum;
import com.leilei.entity.ResultVO;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;

/**
 * @author lei
 * @create 2022-06-14 21:39
 * @desc 权限拦截器
 **/
@Log4j2
public class AuthInterceptor implements HandlerInterceptor {
    /**
     * 网关标识
     */
    public static final String GATEWAY = "GATEWAY";

    /**
     * 网管处转发的认证请求头
     */
    public static final String GATEWAY_AUTHORIZATION = "GATEWAY_AUTHORIZATION";
    private final AuthProperties authProperties;
    private final RestTemplate restTemplate;

    public static final ThreadLocal<LoginUserVO> USER = new TransmittableThreadLocal<>();

    public AuthInterceptor(AuthProperties authProperties, RestTemplate restTemplate) {
        this.authProperties = authProperties;
        this.restTemplate = restTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 内外部接口拦截
            String gatewayFlag = request.getHeader(GATEWAY);
            AccessJwt annotation = handlerMethod.getMethodAnnotation(AccessJwt.class);
            if (authProperties.getInsideOrOutside()) {
                if (StrUtil.isNotBlank(gatewayFlag) && annotation == null) {
                    return writeResponseInfo(response, ResponseCodeEnum.NOT_PERMISSION_INNER);
                }
            }
            if (annotation == null) {
                return true;
            }
            // 网关处请求
            if (StrUtil.isNotBlank(gatewayFlag) && annotation.jwt()) {
                String authorization = request.getHeader(GATEWAY_AUTHORIZATION);
                if (StrUtil.isBlank(authorization)) {
                    return writeResponseInfo(response, ResponseCodeEnum.NOT_LOGIN);
                }
                //指定header去校验中心进行校验JWT
                HttpHeaders headers = new HttpHeaders();
                headers.set(HttpHeaders.AUTHORIZATION, authorization);
                HttpEntity<String> httpEntity = new HttpEntity<>(headers);
                ResponseEntity<ResultVO<LoginUserVO>> exchange = restTemplate.exchange(authProperties.getAuthUrl(), HttpMethod.GET, httpEntity, new ParameterizedTypeReference<ResultVO<LoginUserVO>>() {
                });
                ResultVO<LoginUserVO> body = exchange.getBody();
                if (body == null) {
                    return writeResponseInfo(response, ResponseCodeEnum.TOKEN_ERROR);
                }
                if (body.checkResult()) {
                    LoginUserVO data = body.getData();
                    USER.set(data);
                    return true;
                }
                Integer code = body.getCode();
                String message = body.getMessage();
                return writeResponseInfo(response, code, message);
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        USER.remove();
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }

    public boolean writeResponseInfo(HttpServletResponse response, Integer code, String message) {

        ResultVO<Object> resultVO = ResultVO.failMessage(code, message);
        writeResp(response, resultVO);
        return false;
    }

    public boolean writeResponseInfo(HttpServletResponse response, ResponseCodeEnum responseCodeEnum) {
        ResultVO<Object> resultVO = ResultVO.enumData(responseCodeEnum);
        writeResp(response, resultVO);
        return false;
    }

    private void writeResp(HttpServletResponse response, ResultVO<Object> resultVO) {
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        try (PrintWriter writer = response.getWriter()) {
            writer.append(JSON.toJSONString(resultVO, SerializerFeature.WriteMapNullValue));
            writer.flush();
        } catch (Exception e) {
            log.error("拦截输出异常 ex:{}", e.getMessage());
        }
    }
}
